/**
 * type.cpp
 */
#include "common/exception.h"
#include "type/bigint_type.h"
#include "type/boolean_type.h"
#include "type/decimal_type.h"
#include "type/integer_type.h"
#include "type/smallint_type.h"
#include "type/tinyint_type.h"
#include "type/value.h"
#include "type/varlen_type.h"

namespace cmudb {

Type *Type::kTypes[] = {
    new Type(TypeId::INVALID),
    new BooleanType(),
    new TinyintType(),
    new SmallintType(),
    new IntegerType(TypeId::INTEGER),
    new BigintType(),
    new DecimalType(),
    new VarlenType(TypeId::VARCHAR),
};

// Get the size of this data type in bytes
uint64_t Type::GetTypeSize(const TypeId type_id) {
  switch (type_id) {
  case BOOLEAN:
  case TINYINT:
    return 1;
  case SMALLINT:
    return 2;
  case INTEGER:
    return 4;
  case BIGINT:
  case DECIMAL:
  case TIMESTAMP:
    return 8;
  case VARCHAR:
    return 0;
  default:
    break;
  }
  throw Exception(EXCEPTION_TYPE_UNKNOWN_TYPE, "Unknown type.");
}

bool Type::IsCoercableFrom(const TypeId type_id) const {
  switch (type_id_) {
  case INVALID:
    return false;
  case BOOLEAN:
    return true;
  case TINYINT:
  case SMALLINT:
  case INTEGER:
  case BIGINT:
  case DECIMAL:
    switch (type_id) {
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
    case DECIMAL:
    case VARCHAR:
      return true;
    default:
      return false;
    }
    break;
  case TIMESTAMP:
    return (type_id == VARCHAR || type_id == TIMESTAMP);
  case VARCHAR:
    switch (type_id) {
    case BOOLEAN:
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
    case DECIMAL:
    case TIMESTAMP:
    case VARCHAR:
      return true;
    default:
      return false;
    }
    break;
  default:
    return (type_id == type_id_);
  } // END SWITCH
}

std::string Type::TypeIdToString(const TypeId type_id) {
  switch (type_id) {
  case INVALID:
    return "INVALID";
  case BOOLEAN:
    return "BOOLEAN";
  case TINYINT:
    return "TINYINT";
  case SMALLINT:
    return "SMALLINT";
  case INTEGER:
    return "INTEGER";
  case BIGINT:
    return "BIGINT";
  case DECIMAL:
    return "DECIMAL";
  case TIMESTAMP:
    return "TIMESTAMP";
  case VARCHAR:
    return "VARCHAR";
  default:
    return "INVALID";
  }
}

Value Type::GetMinValue(TypeId type_id) {
  switch (type_id) {
  case BOOLEAN:
    return Value(type_id, 0);
  case TINYINT:
    return Value(type_id, (int8_t)PELOTON_INT8_MIN);
  case SMALLINT:
    return Value(type_id, (int16_t)PELOTON_INT16_MIN);
  case INTEGER:
    return Value(type_id, (int32_t)PELOTON_INT32_MIN);
  case BIGINT:
    return Value(type_id, (int64_t)PELOTON_INT64_MIN);
  case DECIMAL:
    return Value(type_id, PELOTON_DECIMAL_MIN);
  case TIMESTAMP:
    return Value(type_id, 0);
  case VARCHAR:
    return Value(type_id, "");
  default:
    break;
  }
  throw Exception(EXCEPTION_TYPE_MISMATCH_TYPE, "Cannot get minimal value.");
}

Value Type::GetMaxValue(TypeId type_id) {
  switch (type_id) {
  case BOOLEAN:
    return Value(type_id, 1);
  case TINYINT:
    return Value(type_id, (int8_t)PELOTON_INT8_MAX);
  case SMALLINT:
    return Value(type_id, (int16_t)PELOTON_INT16_MAX);
  case INTEGER:
    return Value(type_id, (int32_t)PELOTON_INT32_MAX);
  case BIGINT:
    return Value(type_id, (int64_t)PELOTON_INT64_MAX);
  case DECIMAL:
    return Value(type_id, PELOTON_DECIMAL_MAX);
  case TIMESTAMP:
    return Value(type_id, PELOTON_TIMESTAMP_MAX);
  case VARCHAR:
    return Value(type_id, nullptr, 0, false);
  default:
    break;
  }
  throw Exception(EXCEPTION_TYPE_MISMATCH_TYPE, "Cannot get max value.");
}

CmpBool Type::CompareEquals(const Value &left __attribute__((unused)),
                            const Value &right __attribute__((unused))) const {
  throw NotImplementedException("CompareEquals not implemented");
}

CmpBool Type::CompareNotEquals(const Value &left __attribute__((unused)),
                               const Value &right
                               __attribute__((unused))) const {
  throw NotImplementedException("CompareNotEquals not implemented");
}

CmpBool Type::CompareLessThan(const Value &left __attribute__((unused)),
                              const Value &right
                              __attribute__((unused))) const {
  throw NotImplementedException("CompareLessThan not implemented");
}
CmpBool Type::CompareLessThanEquals(const Value &left __attribute__((unused)),
                                    const Value &right
                                    __attribute__((unused))) const {
  throw NotImplementedException("CompareLessThanEqual not implemented");
}
CmpBool Type::CompareGreaterThan(const Value &left __attribute__((unused)),
                                 const Value &right
                                 __attribute__((unused))) const {

  throw NotImplementedException("CompareGreaterThan not implemented");
}
CmpBool Type::CompareGreaterThanEquals(const Value &left
                                       __attribute__((unused)),
                                       const Value &right
                                       __attribute__((unused))) const {

  throw NotImplementedException("CompareGreaterThanEqual not implemented");
}

// Other mathematical functions
Value Type::Add(const Value &left __attribute__((unused)),
                const Value &right __attribute__((unused))) const {
  throw NotImplementedException("Add not implemented");
}

Value Type::Subtract(const Value &left __attribute__((unused)),
                     const Value &right __attribute__((unused))) const {

  throw NotImplementedException("Subtract not implemented");
}

Value Type::Multiply(const Value &left __attribute__((unused)),
                     const Value &right __attribute__((unused))) const {

  throw NotImplementedException("Multiply not implemented");
}

Value Type::Divide(const Value &left __attribute__((unused)),
                   const Value &right __attribute__((unused))) const {

  throw NotImplementedException("Divide not implemented");
}

Value Type::Modulo(const Value &left __attribute__((unused)),
                   const Value &right __attribute__((unused))) const {

  throw NotImplementedException("Modulo not implemented");
}

Value Type::Min(const Value &left __attribute__((unused)),
                const Value &right __attribute__((unused))) const {
  throw NotImplementedException("Min not implemented");
}

Value Type::Max(const Value &left __attribute__((unused)),
                const Value &right __attribute__((unused))) const {
  throw NotImplementedException("Max not implemented");
}

Value Type::Sqrt(const Value &val __attribute__((unused))) const {
  throw NotImplementedException("Sqrt not implemented");
}

Value Type::OperateNull(const Value &val __attribute__((unused)),
                        const Value &right __attribute__((unused))) const {
  throw NotImplementedException("OperateNull not implemented");
}

bool Type::IsZero(const Value &val __attribute__((unused))) const {
  throw NotImplementedException("isZero not implemented");
}
// Is the data inlined into this classes storage space, or must it be accessed
// through an indirection/pointer?
bool Type::IsInlined(const Value &val __attribute__((unused))) const {
  throw NotImplementedException("IsLined not implemented");
}

// Return a stringified version of this value
std::string Type::ToString(const Value &val __attribute__((unused))) const {
  throw NotImplementedException("ToString not implemented");
}

// Serialize this value into the given storage space. The inlined parameter
// indicates whether we are allowed to inline this value into the storage
// space, or whether we must store only a reference to this value. If inlined
// is false, we may use the provided data pool to allocate space for this
// value, storing a reference into the allocated pool space in the storage.
void Type::SerializeTo(const Value &val __attribute__((unused)),
                       char *storage __attribute__((unused))) const {
  throw NotImplementedException("SerializeTo not implemented");
}

// Deserialize a value of the given type from the given storage space.
Value Type::DeserializeFrom(const char *storage __attribute__((unused))) const {
  throw NotImplementedException("DeserializeFrom not implemented");
}

// Create a copy of this value
Value Type::Copy(const Value &val __attribute__((unused))) const {
  throw NotImplementedException("Copy not implemented");
}

Value Type::CastAs(const Value &val __attribute__((unused)),
                   const TypeId type_id __attribute__((unused))) const {
  throw NotImplementedException("CastAs not implemented");
}

// Access the raw variable length data
const char *Type::GetData(const Value &val __attribute__((unused))) const {
  throw NotImplementedException("GetData from value not implemented");
}

// Get the length of the variable length data
uint32_t Type::GetLength(const Value &val __attribute__((unused))) const {
  throw NotImplementedException("GetLength not implemented");
}

// Access the raw varlen data stored from the tuple storage
char *Type::GetData(char *storage __attribute__((unused))) {
  throw NotImplementedException("GetData not implemented");
}

} // namespace cmudb
